package com.ben.multiple.elasticsearch.template;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScore;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScoreQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MultiMatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.json.JsonData;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.CriteriaQueryBuilder;
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightFieldParameters;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

class JavaSearch2Test {
    static ElasticsearchTemplate template;
    static ElasticsearchClient client;
    static String IndexName = "es_db6";
    static IndexCoordinates indexCoordinates = IndexCoordinates.of(IndexName);

    static {
        client = ElasticsearchClients.createImperative(ClientConfiguration.create("192.168.229.141:9200"));
        template = new ElasticsearchTemplate(client);
    }

    public static void main(String[] args) {
        try {
            functionScore();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("=========================================");
        client.shutdown();
        System.exit(5);
    }

    static void functionScore(){
        MatchQuery matchQuery = new MatchQuery.Builder().field("birthAddress").query("湖").build();
        TermQuery termQuery = new TermQuery.Builder().field("name").value("尹本进").build();
        FunctionScore functionScore = new FunctionScore.Builder().filter(new Query(termQuery)).weight(10.0).build();

        FunctionScoreQuery.Builder builder = new FunctionScoreQuery.Builder();
        builder.boostMode(FunctionBoostMode.Sum);
        builder.query(new Query(matchQuery));
        builder.functions(List.of(functionScore));
        FunctionScoreQuery functionScoreQuery = builder.build();

        NativeQuery nativeQuery = NativeQuery.builder().withQuery(new Query(functionScoreQuery)).withPageable(Pageable.ofSize(7)).build();

        SearchHits<HashMap> hits = template.search(nativeQuery, HashMap.class, indexCoordinates);
        System.out.println(hits.getTotalHits());
        for (SearchHit<HashMap> hit : hits) {
            System.out.println(hit.getContent());
            System.out.println(hit.getScore());
        }
    }

    static void highLight() {
        MultiMatchQuery matchQuery = new MultiMatchQuery.Builder().fields(List.of("name", "birthAddress")).query("尹本进").build();
        HighlightFieldParameters highlightFieldParameters = new HighlightFieldParameters.HighlightFieldParametersBuilder().withRequireFieldMatch(false).build();
        HighlightField highlightField = new HighlightField("name", highlightFieldParameters);

        Highlight highlight = new Highlight(List.of(highlightField, new HighlightField("birthAddress", highlightFieldParameters)));

        NativeQuery nativeQuery = NativeQuery.builder().withQuery(new Query(matchQuery)).withHighlightQuery(new HighlightQuery(highlight, HashMap.class)).build();
        SearchHits<HashMap> hits = template.search(nativeQuery, HashMap.class, indexCoordinates);
        System.out.println(hits.getTotalHits());
        for (SearchHit<HashMap> hit : hits) {
            System.out.println(hit.getContent());
            System.out.println(hit.getHighlightFields());
        }
    }

    static void sortPage() {
        Criteria criteria = Criteria.where("birthLocation").within(new GeoPoint(31.0, 118.0), "50km");
        CriteriaQueryBuilder queryBuilder = new CriteriaQueryBuilder(criteria);
        GeoDistanceOrder distanceOrder = new GeoDistanceOrder("birthLocation", new GeoPoint(31, 118));
        distanceOrder.withUnit("km").with(Sort.Direction.ASC);
        queryBuilder.withSort(Sort.by(distanceOrder));
        queryBuilder.withPageable(PageRequest.of(0, 5));

        CriteriaQuery query = queryBuilder.build();
        SearchHits<HashMap> hits = template.search(query, HashMap.class, indexCoordinates);
        System.out.println(hits.getTotalHits());
        for (SearchHit<HashMap> hit : hits) {
            System.out.println(hit.getContent());
            System.out.println(hit.getSortValues());
        }
    }

    static void bool() {
        BoolQuery.Builder builder = QueryBuilders.bool();
        List<Query> mustList = new ArrayList<>();
        List<Query> shouldList = new ArrayList<>();
        List<Query> filterList = new ArrayList<>();

        MatchQuery matchQuery2 = new MatchQuery.Builder().field("birthAddress").query("本").build();
        RangeQuery rangeQuery2 = new RangeQuery.Builder().field("age").gte(JsonData.of(60)).build();

        MatchQuery matchQuery1 = new MatchQuery.Builder().field("birthAddress").query("尹").build();
        RangeQuery rangeQuery1 = new RangeQuery.Builder().field("birthday").gte(JsonData.of("2220-01-01")).build();

        mustList.add(new Query(rangeQuery2));
        shouldList.add(new Query(matchQuery1));
        shouldList.add(new Query(rangeQuery1));
        filterList.add(new Query(matchQuery2));

        builder.must(mustList);
        builder.should(shouldList);
        builder.filter(filterList);

        BoolQuery boolQuery = builder.build();
        NativeQuery nativeQuery = NativeQuery.builder().withQuery(new Query(boolQuery)).build();
        SearchHits<HashMap> hits = template.search(nativeQuery, HashMap.class, indexCoordinates);
        long totalHits = hits.getTotalHits();
        System.out.println(totalHits);
        for (SearchHit<HashMap> hit : hits) {
            System.out.println(hit.getContent());
        }
    }

    static void geoSearch() {
        Criteria criteria = Criteria.where("birthLocation").within(new GeoPoint(31.0, 118.0), "30km");
        CriteriaQuery query = new CriteriaQueryBuilder(criteria).build();
        SearchHits<HashMap> hits = template.search(query, HashMap.class, indexCoordinates);
        for (SearchHit<HashMap> hit : hits) {
            System.out.println(hit.getContent());
        }
    }

    static void search() {
        Criteria contains = Criteria.where("name").contains("武汉");
        Criteria or1 = contains.or(Criteria.where("name").startsWith("尹本进"));
        CriteriaQuery query = new CriteriaQueryBuilder(or1).build();
        SearchHits<HashMap> hits = template.search(query, HashMap.class, indexCoordinates);
        for (SearchHit<HashMap> hit : hits) {
            System.out.println(hit.getContent());
        }
    }


    private static void initData() {
        List<Model> list = Model.create(1000);
        for (Model model : list) {
            Map<String, Object> map = Model.buildMap(model);
            Document document = Document.from(map);
            UpdateQuery updateQuery = UpdateQuery.builder(String.valueOf(model.getId()))
                    .withDocument(document).withDocAsUpsert(true).build();
            template.update(updateQuery, indexCoordinates);
        }
    }

    private static void initIndex() {
        IndexOperations indexOperations = template.indexOps(indexCoordinates);
        Map<String, Object> settings = new LinkedHashMap<>();
        Map<String, Object> index = new LinkedHashMap<>();
        index.put("analysis.analyzer.default.type", "ik_max_word");
        settings.put("index", index);

        Map<String, Map<String, Object>> map = new LinkedHashMap<>();
        Map<String, Object> id = new LinkedHashMap<>();
        id.put("type", "integer");
        id.put("index", true);
        id.put("store", true);
        map.put("id", id);

        Map<String, Object> name = new LinkedHashMap<>();
        name.put("type", "keyword");
        name.put("index", true);
        name.put("store", true);
        map.put("name", name);

        Map<String, Object> age = new LinkedHashMap<>();
        age.put("type", "integer");
        age.put("index", true);
        age.put("store", true);
        map.put("age", age);

        Map<String, Object> birthday = new LinkedHashMap<>();
        birthday.put("type", "date");
        birthday.put("index", true);
        birthday.put("store", true);
        birthday.put("format", "yyyy-MM-dd");
        map.put("birthday", birthday);

        Map<String, Object> birthLocation = new LinkedHashMap<>();
        birthLocation.put("type", "geo_point");
//        birthLocation.put("index", true);
        birthLocation.put("store", true);
        map.put("birthLocation", birthLocation);

        Map<String, Object> birthAddress = new LinkedHashMap<>();
        birthAddress.put("type", "text");
        birthAddress.put("index", true);
        birthAddress.put("store", true);
        map.put("birthAddress", birthAddress);

        Map<String, Object> hobbit = new LinkedHashMap<>();
        hobbit.put("type", "keyword");
        hobbit.put("index", true);
        hobbit.put("store", true);
        map.put("hobbit", hobbit);

        Map<String, Object> properties = new HashMap<>();
        properties.put("properties", map);
        Document document = Document.from(properties);

        indexOperations.create(settings, document);
    }

}
